<script setup lang="ts">
import { computed, onMounted, reactive, ref, unref, watch } from "vue";
import { round } from "@/utils/common.ts";
import { getCustomTemplateFields } from "@/api/waybill.ts";
import { handleApi } from "@/utils/alert.ts";
import { uploadCustomTemplate } from "@/api/qiniu.ts";

const emits = defineEmits<{
  save: [CustomTemplateFieldDetail[], string, string],
  cancel: []
}>()

export type TemplateEditorProps = {
  img: string;
  paperWidth: number;
  paperHeight: number;
  customAreaWidth: number;
  customAreaHeight: number;
  customAreaLeft: number;
  customAreaTop: number;
  botImg: string;

  fields?: CustomTemplateFieldDetail[];

  readonly?: boolean;
}

// pt 转 mm 的系数: pt = mm / coef_pt2mm
const coef_pt2mm = 0.352778


export type DraggableNode = {
  key?: string;
  text: string;
  // mm_ 表示 毫米 单位, pt_ 表示 pt 单位
  x: number;
  mm_x: number;
  y: number;
  mm_y: number;
  width: number;
  mm_width: number;
  height: number;
  mm_height: number;
  fontFamily: string;
  fontSize: number;
  pt_fontSize: number;
  fontWeight: string;
  fontColor: string;
}

// 生成一个新的默认拖拽节点
const defaultDraggableNode = (): DraggableNode => ({
  text: '',
  x: 0,
  mm_x: 0,
  y: 0,
  mm_y: 0,
  width: 0,
  mm_width: 0,
  height: 0,
  mm_height: 0,
  fontFamily: '宋体',
  fontSize: round(8 / rd.value * coef_pt2mm, 2),
  pt_fontSize: 8,
  fontWeight: 'normal',
  fontColor: '#000000',
})

const props = defineProps<TemplateEditorProps>()

// 可视区域 ref
const viewRef = ref<HTMLDivElement>()
// 拖拽节点 ref
const draggingRef = ref<HTMLDivElement>()
// 背景图片 ref
const bgRef = ref<HTMLImageElement>()
const bgBotRef = ref<HTMLImageElement>()
// 可选字段
const fields = reactive<ICustomTemplateField[]>([])
// 自定义区域
const customArea = reactive({
  ready: false,
  px: [0, 0, 0, 0], // [width, height, left, top]
  mm: [0, 0, 0, 0], // [width, height, left, top]
})

// 像素和毫米的比例 rd = mm / px
const rd = ref(1)

const resizing = ref(false)
const resizeStartPos = ref<{ x: number; y: number, w: number, h: number }>({
  x: 0,
  y: 0,
  w: 0,
  h: 0,
})

// 是否正在拖拽
const dragging = ref(false)
// 当前拖拽的位置信息
const position = reactive<{ x: number; y: number, offsetX: number, offsetY: number }>({
  x: 0,
  y: 0,
  offsetX: 0,
  offsetY: 0
})
// 模板中已有的节点
const nodes = reactive<DraggableNode[]>([])
// 拖拽中的节点
const draggingNode = ref<DraggableNode | null>(null)
// 拖拽中的节点在已有节点列表中的索引
const draggingIndex = ref(-1)
// 已选择的节点在已有节点列表中的索引
const selectedIndex = ref(-1)
// 节点属性编辑表单内容
const editorForm = ref<PartialNullable<DraggableNode>>({})
const xmlData = ref([])
// CustomTemplateFieldDetail -> DraggableNode
const field2Node = (field: CustomTemplateFieldDetail): DraggableNode => {
  const r = unref(rd)
  const area = unref(customArea)
  const mm_x = field.left + area.mm[2]
  const mm_y = field.top + area.mm[3]
  return {
    mm_x,
    mm_y,
    mm_width: field.width,
    mm_height: field.height,
    x: mm_x / r,
    y: mm_y / r,
    width: field.width / r,
    height: field.height / r,
    key: field.field_name,
    text: field.text,
    pt_fontSize: field.font_size,
    fontSize: field.font_size * coef_pt2mm / r,
    fontColor: '#000000',
    fontFamily: field.font_family,
    fontWeight: field.bold ? 'bold' : 'normal',
  }
}

// DraggableNode -> CustomTemplateFieldDetail
const node2field = (node: DraggableNode): CustomTemplateFieldDetail => {
  const area = unref(customArea)
  const left = node.mm_x - area.mm[2]
  const top = node.mm_y - area.mm[3]
  return {
    left,
    top,
    field_name: node.key,
    text: node.text,
    width: node.mm_width,
    height: node.mm_height,
    font_size: node.pt_fontSize,
    font_family: node.fontFamily,
    bold: node.fontWeight === 'bold'
  }
}

// 初始化控件
onMounted(async () => {
  const fieldsRes = await getCustomTemplateFields().catch(handleApi)
  if (fieldsRes && fieldsRes.code === 1)
    fields.push(...fieldsRes.data)
  xmlData.value = fieldsRes.data

  console.log(xmlData,'xmlDataxmlDataxmlData')
  // 计算 mm / px
  if (viewRef.value && bgRef.value && bgBotRef.value) {
    const bound = viewRef.value.getBoundingClientRect()
    // bgRef.value.height = bound.height
    bgRef.value.src = props.img
    rd.value = props.paperHeight / bound.height
    bgBotRef.value.src = props.botImg
    // 计算自定义区域的像素位置
    customArea.mm = [props.customAreaWidth, props.customAreaHeight, props.customAreaLeft, props.customAreaTop]
    customArea.px = customArea.mm.map(i => i / rd.value)
    customArea.ready = true

    // 计算已存在的节点位置, 转换成 DraggableNode
    props.fields?.forEach(i => {
      nodes.push(field2Node(i))
    })
  }
  console.log(fields,'fields')
})

// 通过像素单位更新 mm 和 pt 单位
const calcMMAndPt = (node: DraggableNode) => {
  const r = unref(rd)
  node.mm_x = round(node.x * r)
  node.mm_y = round(node.y * r)
  node.mm_width = round(node.width * r)
  node.mm_height = round(node.height * r)
  node.pt_fontSize = round(node.fontSize * r / coef_pt2mm)
}

// 通过 mm 和 pt 单位更新像素单位
const calcPx = (node: DraggableNode) => {
  const r = unref(rd)
  node.x = round(node.mm_x / r)
  node.y = round(node.mm_y / r)
  node.width = round(node.mm_width / r)
  node.height = round(node.mm_height / r)
  node.fontSize = round(node.pt_fontSize / r * coef_pt2mm)
}


// 计算属性: 已选择的节点
const selectedNode = computed(() => {
  if (dragging.value && draggingIndex.value === -1) {
    return draggingNode.value
  } else if (selectedIndex.value !== -1 && nodes?.[selectedIndex.value]) {
    return nodes[selectedIndex.value]
  }
  return null
})

// 选中节点和坐标更新时, 将数据同步到表单中
watch([selectedNode, position], ([node, ]) => {
  editorForm.value = node ? { ...node } : {}
})

// 拖拽过程, 更新坐标
const onMouseMove = (ev: MouseEvent) => {
  if (dragging.value) {
    const pos = viewRef.value?.getBoundingClientRect()
    if (pos) {
      position.x = ev.x - pos.x
      position.y = ev.y - pos.y

      if (draggingIndex.value !== -1 && nodes?.[draggingIndex.value]) {
        nodes[draggingIndex.value].x = position.x - position.offsetX
        nodes[draggingIndex.value].y = position.y - position.offsetY
        calcMMAndPt(nodes[draggingIndex.value])
      } else if (draggingNode.value) {
        draggingNode.value.x = position.x
        draggingNode.value.y = position.y
        calcMMAndPt(draggingNode.value)
      }
    }
  } else if (resizing.value) {
    if (nodes?.[selectedIndex.value]) {
      position.x = ev.x
      position.y = ev.y

      nodes[selectedIndex.value].width = Math.max(resizeStartPos.value.w + (ev.x - resizeStartPos.value.x), 15)
      nodes[selectedIndex.value].height = Math.max(resizeStartPos.value.h + (ev.y - resizeStartPos.value.y), 15)
      calcMMAndPt(nodes[selectedIndex.value])
    }
  }
}

const startResize = (ev: MouseEvent, i: number) => {
  if (props.readonly) return
  selectedIndex.value = i
  resizing.value = true
  resizeStartPos.value = {
    x: ev.x,
    y: ev.y,
    w: nodes[i].width,
    h: nodes[i].height
  }
}
const endResize = () => {
  resizing.value = false
}

// 开始拖拽
const startDrag = (ev: MouseEvent, text: string, i = -1, key?: string) => {
  if (props.readonly) return
  dragging.value = true
  position.offsetX = i !== -1 ? ev.offsetX : 0
  position.offsetY = i !== -1 ? ev.offsetY : 0
  draggingIndex.value = i
  selectedIndex.value = i
  if (i === -1) {
    draggingNode.value = { ...defaultDraggableNode(), text, key }
  }
}

const endEv = () => {
  endDrag();
  endResize();
}

// 结束拖拽事件
const endDrag = () => {
  if (dragging.value && draggingNode.value && draggingIndex.value === -1) {
    let width = 0
    let height = 0
    if (draggingRef.value) {
      const bound = draggingRef.value.getBoundingClientRect()
      width = round(bound.width)
      height = round(bound.height)
    }
    const node = { ...draggingNode.value, width, height }
    calcMMAndPt(node)
    nodes.push(node)
  }
  draggingIndex.value = -1
  dragging.value = false
}

// 选中节点
const selectNode = (i: number) => {
  selectedIndex.value = i
}

// 移除当前选择的节点
const removeNode = () => {
  if (selectedIndex.value !== -1) {
    nodes.splice(selectedIndex.value, 1)
    selectedIndex.value = -1
  }
}

// 更新当前编辑的内容到节点
const updateNode = () => {
  if (selectedIndex.value !== -1) {
    const form = unref(editorForm) as DraggableNode
    calcPx(form)
    nodes[selectedIndex.value] = {
      ...nodes[selectedIndex.value],
      ...form
    }
  }
}

// 生成模板内容
const templateGenerate = (fields: CustomTemplateFieldDetail[]) => {
  const inner = fields.map(i => {
    const ConfigXmlData = xmlData.value.find(x => x.name === i.field_name)
    if(!ConfigXmlData) console.log(i,'i.field_name')
    return `<text id="${i.field_name}" left="${i.left}" top="${i.top}" width="${i.width}" height="${i.height}" presentation="${i.text}" style="fontFamily:${i.font_family};fontSize:${i.font_size}pt;letterSpacing:0.2mm;fontWeight:${i.bold ? 'bold' : 'normal'};fontColor:#000000;lineHeight:140%;fontUnderline:false;fontItalic:false;alpha:255;align:left;valign:top">
            ${ConfigXmlData?.extend}
        </text>`
  }).join('\n')
  return `<?xml version="1.0" encoding="UTF-8"?>
    <layout id="CUSTOM_AREA" left="${props.customAreaLeft}" top="${props.customAreaTop}" width="${props.customAreaWidth}" height="${props.customAreaHeight}">
    ${inner}
    </layout>`
}


const onSave = async () => {
  const fields = nodes.map(i => node2field(i))
  const template = templateGenerate(fields)
  const res = await uploadCustomTemplate(template).catch(handleApi)
  if (res) {
    emits('save', fields, res, template)
  }
}

</script>

<template>
  <div class="size-full flex flex-col">
    <!-- 编辑表单 -->
    <AForm layout="inline" class="space-y-1 mb-1 flex-none" :disabled="selectedIndex === -1" v-if="!props.readonly">
      <AFormItem label="X" class="mt-1">
        <AInputNumber :min="0" :step="0.1" v-model:value="editorForm.mm_x" @change="updateNode"></AInputNumber>
      </AFormItem>
      <AFormItem label="Y">
        <AInputNumber :min="0" :step="0.1" v-model:value="editorForm.mm_y" @change="updateNode"></AInputNumber>
      </AFormItem>
      <AFormItem label="宽度">
        <AInputNumber :min="0" :step="0.1" v-model:value="editorForm.mm_width" @change="updateNode"></AInputNumber>
      </AFormItem>
      <AFormItem label="高度">
        <AInputNumber :min="0" :step="0.1" v-model:value="editorForm.mm_height" @change="updateNode"></AInputNumber>
      </AFormItem>

      <AFormItem label="字体">
        <ASelect v-model:value="editorForm.fontFamily" @change="updateNode">
          <ASelectOption value="宋体">宋体</ASelectOption>
          <ASelectOption value="黑体">黑体</ASelectOption>
        </ASelect>
      </AFormItem>
      <AFormItem label="字号">
        <ASelect v-model:value="editorForm.pt_fontSize" @change="updateNode">
          <ASelectOption :value="6">6pt</ASelectOption>
          <ASelectOption :value="8">8pt</ASelectOption>
          <ASelectOption :value="9">9pt</ASelectOption>
          <ASelectOption :value="12">12pt</ASelectOption>
          <ASelectOption :value="14">14pt</ASelectOption>
          <ASelectOption :value="16">16pt</ASelectOption>
          <ASelectOption :value="18">18pt</ASelectOption>
          <ASelectOption :value="20">20pt</ASelectOption>
          <ASelectOption :value="22">22pt</ASelectOption>
          <ASelectOption :value="24">24pt</ASelectOption>
          <ASelectOption :value="26">26pt</ASelectOption>
          <ASelectOption :value="28">28pt</ASelectOption>
          <ASelectOption :value="36">36pt</ASelectOption>
          <ASelectOption :value="48">48pt</ASelectOption>
          <ASelectOption :value="72">72pt</ASelectOption>
        </ASelect>
      </AFormItem>
      <AFormItem label="加粗">
        <ASelect v-model:value="editorForm.fontWeight" @change="updateNode">
          <ASelectOption value="normal">不加粗</ASelectOption>
          <ASelectOption value="bold">加粗</ASelectOption>
        </ASelect>
      </AFormItem>

      <AFormItem label="内容">
        <AInput :disabled="selectedIndex === -1 || !!selectedNode?.key" v-model:value="editorForm.text" @change="updateNode"></AInput>
      </AFormItem>

      <AFormItem>
        <APopconfirm
          description="确定要删除该节点吗?"
          ok-text="确定"
          cancel-text="取消"
          @confirm="removeNode"
          :disabled="selectedIndex === -1 || !selectedNode"
        >
          <AButton :disabled="selectedIndex === -1 || !selectedNode" danger>删除节点</AButton>
        </APopconfirm>
      </AFormItem>
    </AForm>

    <div class="flex-auto flex" @mouseup="endEv" @mousemove="onMouseMove" :class="{ 'cursor-move': dragging }">

      <!-- 预览区域 -->
      <div class="flex-auto relative"
           ref="viewRef"
           @click.self="selectNode(-1)"
      >
        <img style="width: 344px" ref="bgRef" alt="preview" src="" class="absolute top-0 left-0">
        <img style="width: 344px" ref="bgBotRef" alt="preview" src="" class="absolute bottom-0 left-0">

        <!-- 自定义区域 -->
        <div
          v-if="customArea.ready"
          class="border border-dashed border-gray-400 box-border absolute bg-transparent"
          :style="{
            width: customArea.px[0] + 'px',
            height: customArea.px[1] + 'px',
            left: customArea.px[2] + 'px',
            top: customArea.px[3] + 'px',
          }"
        ></div>

        <!-- 新增拖拽节点 -->
        <div
          ref="draggingRef"
          v-if="dragging && draggingNode && draggingIndex === -1"
          class="border-2 border-dashed border-red-600 select-none cursor-move absolute box-border text-nowrap"
          :style="{
            top: draggingNode.y + 'px',
            left: draggingNode.x + 'px',
            'font-family': draggingNode.fontFamily,
            'font-size': draggingNode.fontSize + 'px'
          }"
        >
          {{ draggingNode?.text }}
        </div>

        <!-- 已存在的拖拽节点 -->
        <div
          v-for="(node, i) of nodes"
          class="border-2 border-dashed select-none absolute box-border text-wrap break-all"
          :class="{
            'border-red-500': !props.readonly && selectedIndex === i,
            'border-blue-500': !props.readonly && selectedIndex !== i,
            'font-bold': node.fontWeight !== 'normal',
            'cursor-move': !props.readonly,
            'border-transparent': props.readonly
          }"
          :style="{
            left: node.x + 'px',
            top: node.y + 'px',
            'font-family': node.fontFamily,
            'font-size': node.fontSize + 'px',
            width: node.width ? node.width + 'px' : 'auto',
            height: node.height ? node.height + 'px' : 'auto'
          }"
          @mousedown="startDrag($event, node.text, i, node.key)"
          @click.stop="selectNode(i)"
        >
          <div class="size-full relative overflow-hidden">{{ node.text }}</div>

          <div
            class="size-[10px] absolute right-[-5px] bottom-[-5px] cursor-nwse-resize"
            @mousedown.stop="startResize($event, i)"
          ></div>
        </div>

      </div>

      <!-- 空间列表 -->
      <div class="flex-none w-[250px] h-full overflow-y-auto px-1 space-y-1" v-if="!props.readonly">
        <div
          class="border border-solid border-gray-300 p-2 rounded relative select-none cursor-move"
          @mousedown="startDrag($event, '自定义文本', -1)"
        >
          自定义文本
        </div>
        <div
          v-for="field in fields"
          class="border border-solid border-gray-300 p-2 rounded relative select-none cursor-move"
          @mousedown="startDrag($event, `[${field.describe}]`, -1, field.name)"
        >
          {{ field.describe }}
        </div>
      </div>
    </div>

    <div class="flex-none p-2 flex space-x-2" v-if="!props.readonly">
      <AButton type="primary" @click="onSave">保存模板</AButton>
      <AButton danger @click="emits('cancel')">取消</AButton>
    </div>
  </div>
</template>

<style scoped>
.ant-select.ant-select-in-form-item {
  min-width: 100px;
}
</style>
